#include "CJavascriptCounter.h"

CJavascriptCounter::CJavascriptCounter()
{
	QuoteStart = "\"'";	// As per comment in CCodeCounter.h
	QuoteEnd = "\"'";	// As per comment in CCodeCounter.h

	LineCommentStart.push_back("//");

	BlockCommentStart.push_back("/*");
	BlockCommentEnd.push_back("*/");

	classtype = JAVASCRIPT;
	language_name = "JavaScript";

	file_extension.push_back(".js");
	file_extension.push_back(".*js");

	data_name_list.push_back("abstract");
	data_name_list.push_back("implements");
	data_name_list.push_back("void");
	data_name_list.push_back("float");
	data_name_list.push_back("private");
	data_name_list.push_back("enum");
	data_name_list.push_back("class");
	data_name_list.push_back("long");
	data_name_list.push_back("double");
	data_name_list.push_back("static");
	data_name_list.push_back("interface");
	data_name_list.push_back("char");
	data_name_list.push_back("short");
	data_name_list.push_back("int");
	data_name_list.push_back("byte");
	data_name_list.push_back("public");
	data_name_list.push_back("instanceOf");
	data_name_list.push_back("boolean");
	data_name_list.push_back("protected");
	data_name_list.push_back("const");

	exec_name_list.push_back("break");
	exec_name_list.push_back("continue");
	exec_name_list.push_back("do");
	exec_name_list.push_back("for");
	exec_name_list.push_back("import");
	exec_name_list.push_back("new");
	exec_name_list.push_back("this");
	exec_name_list.push_back("case");
	exec_name_list.push_back("default");
	exec_name_list.push_back("else");
	exec_name_list.push_back("function");
	exec_name_list.push_back("in");
	exec_name_list.push_back("return");
	exec_name_list.push_back("typeof");
	exec_name_list.push_back("while");
	exec_name_list.push_back("comment");
	exec_name_list.push_back("delete");
	exec_name_list.push_back("export");
	exec_name_list.push_back("if");
	exec_name_list.push_back("label");
	exec_name_list.push_back("switch");
	exec_name_list.push_back("var");
	exec_name_list.push_back("with");
	exec_name_list.push_back("synchronized");
	exec_name_list.push_back("false");
	exec_name_list.push_back("native");
	exec_name_list.push_back("throws");
	exec_name_list.push_back("final");
	exec_name_list.push_back("null");
	exec_name_list.push_back("transient");
	exec_name_list.push_back("package");
	exec_name_list.push_back("true");
	exec_name_list.push_back("goto");
	exec_name_list.push_back("catch");
	exec_name_list.push_back("throw");
	exec_name_list.push_back("extends");
	exec_name_list.push_back("try");
	exec_name_list.push_back("finally");
	exec_name_list.push_back("debugger");
	exec_name_list.push_back("super");
	exec_name_list.push_back("alert");
	exec_name_list.push_back("eval");
	exec_name_list.push_back("Link");
	exec_name_list.push_back("outerHeight");
	exec_name_list.push_back("scrollTo");
	exec_name_list.push_back("Anchor");
	exec_name_list.push_back("FileUpload");
	exec_name_list.push_back("location");
	exec_name_list.push_back("outerWidth");
	exec_name_list.push_back("Select");
	exec_name_list.push_back("Area");
	exec_name_list.push_back("find");
	exec_name_list.push_back("Location");
	exec_name_list.push_back("Packages");
	exec_name_list.push_back("self");
	exec_name_list.push_back("arguments");
	exec_name_list.push_back("focus");
	exec_name_list.push_back("locationbar");
	exec_name_list.push_back("pageXoffset");
	exec_name_list.push_back("setInterval");
	exec_name_list.push_back("Array");
	exec_name_list.push_back("Form");
	exec_name_list.push_back("Math");
	exec_name_list.push_back("pageYoffset");
	exec_name_list.push_back("setTimeout");
	exec_name_list.push_back("assign");
	exec_name_list.push_back("Frame");
	exec_name_list.push_back("menubar");
	exec_name_list.push_back("parent");
	exec_name_list.push_back("status");
	exec_name_list.push_back("blur");
	exec_name_list.push_back("frames");
	exec_name_list.push_back("MimeType");
	exec_name_list.push_back("parseFloat");
	exec_name_list.push_back("statusbar");
	exec_name_list.push_back("Boolean");
	exec_name_list.push_back("Function");
	exec_name_list.push_back("moveBy");
	exec_name_list.push_back("parseInt");
	exec_name_list.push_back("stop");
	exec_name_list.push_back("Button");
	exec_name_list.push_back("getClass");
	exec_name_list.push_back("moveTo");
	exec_name_list.push_back("Password");
	exec_name_list.push_back("String");
	exec_name_list.push_back("callee");
	exec_name_list.push_back("Hidden");
	exec_name_list.push_back("name");
	exec_name_list.push_back("personalbar");
	exec_name_list.push_back("Submit");
	exec_name_list.push_back("caller");
	exec_name_list.push_back("history");
	exec_name_list.push_back("NaN");
	exec_name_list.push_back("Plugin");
	exec_name_list.push_back("sun");
	exec_name_list.push_back("captureEvents");
	exec_name_list.push_back("History");
	exec_name_list.push_back("navigate");
	exec_name_list.push_back("print");
	exec_name_list.push_back("taint");
	exec_name_list.push_back("Checkbox");
	exec_name_list.push_back("home");
	exec_name_list.push_back("navigator");
	exec_name_list.push_back("prompt");
	exec_name_list.push_back("Text");
	exec_name_list.push_back("clearInterval");
	exec_name_list.push_back("Image");
	exec_name_list.push_back("Navigator");
	exec_name_list.push_back("prototype");
	exec_name_list.push_back("Textarea");
	exec_name_list.push_back("clearTimeout");
	exec_name_list.push_back("Infinity");
	exec_name_list.push_back("netscape");
	exec_name_list.push_back("Radio");
	exec_name_list.push_back("toolbar");
	exec_name_list.push_back("close");
	exec_name_list.push_back("innerHeight");
	exec_name_list.push_back("Number");
	exec_name_list.push_back("ref");
	exec_name_list.push_back("top");
	exec_name_list.push_back("closed");
	exec_name_list.push_back("innerWidth");
	exec_name_list.push_back("ObjExp");
	exec_name_list.push_back("toString");
	exec_name_list.push_back("confirm");
	exec_name_list.push_back("isFinite");
	exec_name_list.push_back("onBlur");
	exec_name_list.push_back("releaseEvents");
	exec_name_list.push_back("unescape");
	exec_name_list.push_back("constructor");
	exec_name_list.push_back("isNan");
	exec_name_list.push_back("onError");
	exec_name_list.push_back("Reset");
	exec_name_list.push_back("untaint");
	exec_name_list.push_back("Date");
	exec_name_list.push_back("java");
	exec_name_list.push_back("onFocus");
	exec_name_list.push_back("resizeBy");
	exec_name_list.push_back("unwatch");
	exec_name_list.push_back("defaultStatus");
	exec_name_list.push_back("JavaArray");
	exec_name_list.push_back("onLoad");
	exec_name_list.push_back("resizeTo");
	exec_name_list.push_back("valueOf");
	exec_name_list.push_back("document");
	exec_name_list.push_back("JavaClass");
	exec_name_list.push_back("onUnload");
	exec_name_list.push_back("routeEvent");
	exec_name_list.push_back("watch");
	exec_name_list.push_back("Document");
	exec_name_list.push_back("JavaObject");
	exec_name_list.push_back("open");
	exec_name_list.push_back("scroll");
	exec_name_list.push_back("window");
	exec_name_list.push_back("Element");
	exec_name_list.push_back("JavaPackage");
	exec_name_list.push_back("opener");
	exec_name_list.push_back("scrollbars");
	exec_name_list.push_back("Window");
	exec_name_list.push_back("escape");
	exec_name_list.push_back("length");
	exec_name_list.push_back("Option");
	exec_name_list.push_back("scrollBy");

	flow_control_keywords.push_back("if");
	flow_control_keywords.push_back("for");
	flow_control_keywords.push_back("while");
}

int CJavascriptCounter::CountDirectiveSLOC(filemap* fmap, results* result, filemap* fmapBak)
{
	bool contd = false, trunc_flag = false;
	size_t idx, strSize;
	if(classtype == DATAFILE) return 0;
	int cnt = 0;
	string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";
	string strDirLine = "";
	filemap::iterator itfmBak = fmapBak->begin();

	for (filemap::iterator iter = fmap->begin(); iter!=fmap->end(); iter++, itfmBak++)
	{
		if (CUtil::CheckBlank(iter->line)) continue;

		cnt = 0;
		CUtil::CountTally(" " + iter->line, directive, cnt, 1, exclude, "", "", &directive_count, result->duplicate);

		if (!contd)
		{	// if not a continuation of a previous directive
			for(vector<string>::iterator viter = directive.begin(); viter != directive.end(); viter++)
			{
//				if ( (idx = iter->line.find((*viter), 0)) != string::npos && idx == 0)
				if ( ((idx = CUtil::FindKeyword(iter->line, *viter)) != string::npos) 
					  && idx == 0) // VN: Fixed to ensure the keyword stands alone, avoid, e.g., #ifabc
				{
					contd = true;
					break;
   		        }
        	}
			if (contd)
			{
				strSize = CUtil::TruncateLine(itfmBak->line.length(), 0, result->lsloc_truncate, trunc_flag);
				if (strSize > 0)
					strDirLine = itfmBak->line.substr(0, strSize);
				result->directive_lines[PHY]++;
			}	// incrementing the physical directive lines
 		}
		else
		{	// continuation of a previous directive
			strSize = CUtil::TruncateLine(itfmBak->line.length(), strDirLine.length(), result->lsloc_truncate, trunc_flag);
			if (strSize > 0)
				strDirLine += "\n" + itfmBak->line.substr(0, strSize);
			result->directive_lines[PHY]++;  // incrementing the physical lines for a directive
		}
	
		if (contd)
		{	// if a directive or continuation of a directive
			if (iter->line[iter->line.length()-1] != ',' && iter->line[iter->line.length()-1] != '\\')
			{
				contd = false;
				result->directive_lines[LOG]++;
				result->addSLOC(strDirLine, trunc_flag);  //add another Logical directive line, should also have type
			}	// if no continuation symbol found
			iter->line = "";
		}
	}
	return 1;
}

// Special Processing(w/ some revision to logic)
// Data Processing(works the same as before)
// Exec Processing(works the same as before)
// IMPORTANT NOTE: all the  blank lines +
//							whole line comments +
//							line with compiler directive
//				   should have been blanked from filemap by previous processing
//                 before reaching this function
// fmapBak is same as fmat except it contains unmodified quoted strings
int CJavascriptCounter::LanguageSpecificProcess(filemap* fmap, results* result, filemap* fmapBak)
{
	int			paren_count			= 0	   ;
	bool		for_flag			= false;
	bool		found_forifwhile	= false;
	bool		found_while			= false;
	char		prev_char			= 0;
//	bool		data_flag			= false;
	bool		data_continue		= false;
	bool		inArrayDec			= false;
	string		strLSLOC			= ""   ;
	string		strLSLOCBak			= "";

	filemap::iterator fit, fitbak;
	string line, lineBak;

	int phys_exec_lines = 0;
	int phys_data_lines = 0;
	int temp_lines = 0;

	// two strings used for string match
	string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";

	//std::cout << "  Phys_Data: " << result->data_lines[PHY] << "  Phys_Exec: " << result->exec_lines[PHY] << "\n\n";
	for (fit = fmap->begin(), fitbak = fmapBak->begin(); fit != fmap->end(); fit++, fitbak++)
	{
		line = fit->line;

		// insert blank at the beginning(for searching keywords
		line = ' ' + line;
		lineBak = ' ' + fitbak->line;

		// Do not process blank lines
		// Blank line means blank_line/comment_line/directive
		if (!CUtil::CheckBlank(line))
		{
			LSLOC(result, line, lineBak, strLSLOC, strLSLOCBak, paren_count, for_flag, found_forifwhile, found_while,
				prev_char, data_continue, temp_lines, phys_exec_lines, phys_data_lines, inArrayDec);
		
			/*****************************************************************************/
			/*                                                                           */
			/* Keyword Counting:                                                         */
			/*   - Number of Data Keywords.                                              */
			/*   - Number of Executable Keywords.                                        */
			/* Notes:                                                                    */
			/*   -                                                                       */
			/*                                                                           */
			/*****************************************************************************/
			int cnt=0;
			CUtil::CountTally(line, exec_name_list, cnt, 1, exclude, "", "", &exec_name_count, result->duplicate);

			result->exec_lines[PHY] += phys_exec_lines;		// Update physical SLOC lines
			phys_exec_lines = 0;

			result->data_lines[PHY] += phys_data_lines;
			phys_data_lines = 0;

		}	// if(not a blank)

	}	//for(fit)
	return 1;
}

// Method: LSLOC(...)
// Description: determine and extract logical SLOC to place in the result variable
//              using addSLOC function. Each time the addSLOC function is called,
//              a new logical SLOC is added. This function assumes that the directive
//              is handled before it is called.
// -----------------------------------------
// Revision  Date     Author  Modification
// Rev-1     8/9/08   VN      Original author
// Rev-2     12/1/08  PG      Added capability to count logical/physical SLOC
// Rev-3     12/12/08 VN	  Added lineBak and strSLOCBak to store values before being replaced
//                              all quoted strings; this supports comparision
void CJavascriptCounter::LSLOC(results* result, string line, string lineBak, string &strLSLOC, string &strLSLOCBak, int &paren_cnt,
							   bool &forflag, bool &found_forifwhile, bool &found_while, char &prev_char, bool &data_continue,
							   int &temp_lines, int &phys_exec_lines, int &phys_data_lines, bool &inArrayDec) 
{
	// paren_cnt is used with 'for' statement only

	size_t start = 0; //starting index of the working string
	size_t i = 0, strSize;
	bool found_do, found_else, trunc_flag = false;
//	char prev_char = 0;
	string exclude = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$:";
	int cnt=0;

	string tmp = CUtil::TrimString(strLSLOC);

	// do, try
	found_do = (CUtil::FindKeyword(tmp, "do") != string::npos);
	found_do = (found_do || (CUtil::FindKeyword(tmp, "try") != string::npos));
	// else is treated differently, else is included in SLOC, do and try are not
	found_else = (CUtil::FindKeyword(tmp, "else") != string::npos);

	while (i < line.length()) // there may be more than 1 logical SLOC in this line
	{
		switch (line[i])
		{
		case ';': case '{': // LSLOC terminators
			                // ';' for normal executable or declaration statement
			                // '{' for starting a function or 'do' stmt or a block (which is counted)
			
			// Get the previous logical mark until i-1 index is the new LSLOC
			// except 'do' precedes '{'
			// except '}' precedes ';' ??

			if (paren_cnt > 0 && line[i] == ';') // do nothing inside 'for' statement
				break;			
			
			// case 'while(...);', 'while(...) {', and '} while(...);'
			// this case is handled in case ')'
			if (found_while && found_forifwhile) 
			{
				found_while = false;
				found_forifwhile = false;
				start = i + 1;
				break;
			}

			if (line[i] == '{')
			{
				if (prev_char == '=') inArrayDec = true;
				if (inArrayDec) break; //continue until seeing ';'

				// case for(...); and if (...) {
				if (found_forifwhile) // these specials are handled
				{
					found_forifwhile = false;
					start = i + 1;
					break;
				}

				// check if 'do' precedes '{'
				if (!found_do && !found_else)
				{
					// find for 'do' in string before tmp string
					tmp = CUtil::TrimString(line.substr(start, i - start));
					found_do = (tmp == "do"); // Found 'do' statement
					found_do = (found_do || (tmp == "try")); // Found 'try' statement
					// same as else
					found_else = (tmp == "else"); // Found 'else' statement					
				}
				if (found_do || found_else)
				{
					found_do = false;				
					if (!found_else)
					{
						strLSLOC = ""; // everything before 'do', 'try' are cleared
						start = i + 1;
					}
					break; // do not store '{' following 'do'
				}
			}

			if (line[i] == ';' && prev_char == '}')  // wrong, e.g., a[]={1,2,3};
			{
				// check if in array declaration or not
				// if no, skip. Otherwise, complete the SLOC containing array declaration
				if (!inArrayDec)
				{
					start = i + 1;
					break; 
				}
			}

			inArrayDec = false;

			strSize = CUtil::TruncateLine(i + 1 - start, strLSLOC.length(), result->lsloc_truncate, trunc_flag);
			if (strSize > 0)
			{
				strLSLOC += line.substr(start, strSize);
				strLSLOCBak += lineBak.substr(start, strSize);
			}
			result->addSLOC(strLSLOCBak, trunc_flag);

			// P.G. add a logical SLOC
			cnt = 0;
			CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", &data_name_count, result->duplicate);

			temp_lines++;

			if (data_continue == true && line[i] == ';')
			{
				result->data_lines[LOG] += 1;

				data_continue = false;
				phys_data_lines = temp_lines;
			}
			else
			{
				if (cnt > 0 && line[i] == ';')
				{
					result->data_lines[LOG] += 1;

					data_continue = false;
					phys_data_lines = temp_lines;
				}
				else
				{
					result->exec_lines[LOG] += 1;
					
					data_continue = false;
					phys_exec_lines = temp_lines;
				}
			}

			temp_lines = 0;

			strLSLOC = strLSLOCBak = "";

			start = i + 1;

			break;
		case '(':
			if (forflag)
			{
				paren_cnt++;
			}
			else
			{
				// handle 'for', 'foreach', 'while', 'if' the same way
				tmp = CUtil::TrimString(line.substr(start,i));
				if (CUtil::FindKeyword(tmp, "for") != string::npos
					|| CUtil::FindKeyword(tmp, "foreach") != string::npos
					|| CUtil::FindKeyword(tmp, "while")!= string::npos
					|| CUtil::FindKeyword(tmp, "if") != string::npos)
				{
					forflag = true;
					paren_cnt++;

					if (CUtil::FindKeyword(tmp, "while")!= string::npos) 
						found_while = true;
				}
			}
			break;
		case ')':
			if (forflag)
			{
				paren_cnt--;
				if (paren_cnt == 0) 
				{
					// handle 'for', 'foreach', 'while', 'if'
					strSize = CUtil::TruncateLine(i + 1 - start, strLSLOCBak.length(), result->lsloc_truncate, trunc_flag);
					if (strSize > 0)
						strLSLOCBak += lineBak.substr(start, strSize);
					result->addSLOC(strLSLOCBak, trunc_flag);
					strLSLOCBak = strLSLOC = "";

					// P.G. Add a logical SLOC
					result->exec_lines[LOG] += 1;

					phys_exec_lines = temp_lines;
					temp_lines = 0;

					start = i + 1;

					found_forifwhile = true;	
					forflag = false;
				}
			}
			break;
		case '}':
			// Skip '}' when found ';' and then '}'
			//  because '{' is counted already
			// Also, {} is also skipped, counted
			if (prev_char == ';' || prev_char == '{' || prev_char == '}')
			{
				if (!inArrayDec) start = i + 1;
			}
			break;
		} //switch

		if (line[i] != ' ' && line[i] != '\t')
		{
			// if ;}}} --> don't count }}} at all
			// also, if {}}} --> don't count }}} at all
			//if ( !(line[i] == '}' && (prev_char == ';' || prev_char == '{'))) // see case '}' above
			prev_char = line[i];

			if (line[i] != ')' && found_forifwhile) // change to not found if a char appears before
			{
				found_forifwhile = false;			
			}
		}

		i++;
	
	} //while (true)

	tmp = CUtil::TrimString(line.substr(start, i - start));
	strSize = CUtil::TruncateLine(tmp.length(), strLSLOC.length(), result->lsloc_truncate, trunc_flag);
	if (strSize > 0)
	{
		strLSLOC += tmp.substr(0, strSize);
		tmp = CUtil::TrimString(lineBak.substr(start, i - start));
		strLSLOCBak += tmp.substr(0, strSize);
	}
	
	// Need to make sure that we are not beginning to process a new data line
	cnt = 0;
	CUtil::CountTally(strLSLOC, data_name_list, cnt, 1, exclude, "", "", NULL);

	if (cnt > 0)
	{
		data_continue = true;
	}

	if (data_continue == true)
	{
		temp_lines++;
	}

	if ((temp_lines == 0) && (phys_data_lines == 0) && (phys_exec_lines == 0))
	{
		phys_exec_lines = 1;
	}
}

CJavascriptPhpCounter::CJavascriptPhpCounter()
{
	classtype = JAVASCRIPT_PHP;
	language_name = "JavaScript/PHP";

	file_extension.clear();
	file_extension.push_back(".*jsphp");
}

CJavascriptHtmlCounter::CJavascriptHtmlCounter()
{
	classtype = JAVASCRIPT_HTML;
	language_name = "JavaScript/HTML";

	file_extension.clear();
	file_extension.push_back(".*jshtm");
}

CJavascriptJspCounter::CJavascriptJspCounter()
{
	classtype = JAVASCRIPT_JSP;
	language_name = "JavaScript/JSP";

	file_extension.clear();
	file_extension.push_back(".*jsjsp");
}

CJavascriptAspServerCounter::CJavascriptAspServerCounter()
{
	classtype = JAVASCRIPT_ASP_S;
	language_name = "JavaScript/ASP server)";

	file_extension.clear();
	file_extension.push_back(".*jsasps");
}

CJavascriptAspClientCounter::CJavascriptAspClientCounter()
{
	classtype = JAVASCRIPT_ASP_C;
	language_name = "JavaScript/ASP client)";

	file_extension.clear();
	file_extension.push_back(".*jsaspc");
}
